// 枚举其实非常像对象，实现原理就是反向映射，枚举定义之后就不能修改了

// 数字枚举
enum Role {
  Reporter, // 0
  Developer, // 1
  Maintainer, // 2
  Owner = 10, // 10 【可以自定义它的初始值】
  Guest, // 11 【那么后面的枚举成员就会相应的递增】
}
console.log(Role);
console.log(Role.Reporter); // 0
console.log(Role.Owner); // 10
console.log(Role.Guest); // 11

console.log(Role["2"]); // 用值来获取枚举名

// 不能在外部修改枚举成员的值
// Role.Reporter = 2; // 只读属性不能外部赋值

// 字符串枚举
enum Message {
  Success = "恭喜你，成功了",
  Fail = "抱歉，失败了",
}

// 异构枚举【数字枚举和字符串枚举共同使用就是异构枚举，但是这种方式容易引起混淆所以不建议使用】
enum Answer {
  N,
  Y = "Yes",
}

// 枚举成员
/**
 * 枚举成员的分类，枚举成员的类型分为两类一类：
 * 第一种：叫做 const 也就是常量枚举，常量枚举包括三种情况，常量枚举成员呢会在编译的时候计算出结果然后以常量的形式出现在运行时环境
 * 第二种：另外一种类型就是 computed 它的含义就是需要被计算的枚举成员，它就是一些非常量的表达式，这些枚举成员的值呢不会在编译阶段进行计算而会被保留到程序的执行阶段
 */
enum Char {
  // const 常量枚举
  a, // 第一种：没有初始值的情况
  b = Char.a, // 第二种：对已有成员的引用
  c = 1 + 3,
  // computed
  d = Math.random(), // 第三种：一些常量的表达式，在运行时环境才会被计算
  e = "123".length,
  // f // computed 之后的值必须要赋予初始值
}

// 常量枚举【用 const 声明的枚举就是一个常量枚举】
// 常量枚举有一个特性：就是会在编译阶段被移除
// 那么常量枚举的作用是什么呢？当我们不需要一个对象而需要对象的值的时候就可以使用常量枚举，这样会减少我们在编译环境的代码
const enum Month {
  Jan,
  Fed,
  Mar,
}
let month = [Month.Jan, Month.Fed, Month.Mar];
console.log(month); // [0, 1, 2]

// 枚举类型【在某些情况下枚举和枚举成员都可以作为一种单独的类型存在】
enum E {
  a,
  b,
} // 枚举成员没有任何的初始值
enum F {
  a = 0,
  b = 1,
} // 所有成员都是数字枚举
enum G {
  a = "apple",
  b = "banana",
} // 所有成员都是字符串枚举

let e: E = 3;
let f: F = 3; // 我们可以把任意的 number 类型赋值给枚举类型，它的取值呢也可以超出枚举成员的定义
// e===F; // 两种不同类型的枚举是不可以进行比较的
console.log(e,f); // 3, 3

let e1: E.a = 1;
let e2: E.b;
// e1===e2; // e1和e2是永远不能进行比较的
let e3: E.a = 1;
e1===e3; // e1和e3是相同的枚举类型所有可以比较

// 字符串枚举它的取值只能是枚举成员的类型
let g1: G = G.a;
let g2: G.a = G.a; // 这里只能是G.a它自身
